/**
 * @file data_manager.hpp
 * @brief 用户表操作类，用于管理与用户相关的数据库操作。
 */
#pragma once
#include "utils.hpp"
#include <cassert>

class user_table {
    static const uint32_t initial_ladder_score = 1000; // 初始天梯分数
public:
    /**
     * @brief 构造用户表的操作句柄
     * @param host      MySQL 服务器地址
     * @param user      数据库用户名
     * @param passwd    数据库密码
     * @param dbname    要连接的数据库名称
     * @param port      MySQL 服务器端口（默认为 3306）
     */
    user_table(const std::string &host,
        const std::string &user,
        const std::string &passwd,
        const std::string &dbname,
        uint32_t port) {
        // 1. 获取MySQL操作句柄
        _mysql = mysql_util::mysql_create(host, user, passwd, dbname, port);
        // 2. 获取失败是一个非常严重的错误
        assert(_mysql != nullptr);
    }
    ~user_table() {
        // 1. 释放MySQL操作句柄
        mysql_util::mysql_destroy(_mysql);
        // 2. 置空，避免悬空指针
        _mysql = nullptr;
    }

    /**
     * @brief 注册时向用户表新增用户记录
     * @param user  用户信息，包含username和password
     * @return bool  注册成功返回true，失败返回false
     */
    bool register_user(Json::Value &user) {
        // 1. 检查用户注册时提供的用户名和密码是否符合规范
        if (user["username"].isNull()) {
            LOG(ERROR, "注册用户的username字段内容缺失\n");
            return false;
        }
        if (user["password"].isNull()) {
            LOG(ERROR, "注册用户的password字段内容缺失\n");
            return false;
        }
        // 2. 构建插入的SQL语句，使用密码函数加密用户密码
        char sql[4096]{0};
        sprintf(sql, "insert into `user` values(null, '%s', password('%s'), %u, 0, 0);", 
                user["username"].asCString(), user["password"].asCString(), initial_ladder_score);
        // 3. 执行SQL语句，成功返回true，失败返回false并输出错误日志
        if (mysql_util::mysql_execute(_mysql, sql) == false) {
            LOG(ERROR, "向用户表插入用户信息的SQL语句执行失败\n");
            return false;
        }
        return true;
    }

    /**
     * @brief 验证用户是否登录并获取存在用户的信息
     * @param user  登录信息，包含username和password
     * @return bool  验证成功返回true，失败返回false
     */
    bool authenticate_user_login(Json::Value &user) {
        // 1. 检查登录用户提供的用户名和密码是否完整并符合规范
        if (user["username"].isNull()) {
            LOG(ERROR, "登录用户的username字段内容缺失\n");
            return false;
        }
        if (user["password"].isNull()) {
            LOG(ERROR, "登录用户的password字段内容缺失\n");
            return false;
        }
        // 2. 构建查询的SQL语句，以用户名和密码共同作为查询过滤条件（考虑设计user时应该把用户名设计为唯一）
        char sql[1024]{0};
        sprintf(sql, "select uid, score, total_count, win_count from `user` where username='%s' and password=password('%s');", 
                user["username"].asCString(), user["password"].asCString());
        MYSQL_RES *result = nullptr;
        {
            // 3, 4步各自线程安全，连续执行要加锁整体线程安全
            std::unique_lock<std::mutex> lock(_mutex);
            // 3. 执行查询的SQL语句
            if (mysql_util::mysql_execute(_mysql, sql) == false) {
                LOG(ERROR, "用户登录验证的SQL语句执行失败\n");
                return false;
            }
            // 4. 获取查询内容并填充到user参数中
            // 代码执行到这里要么没有结果，要么只有一条结果
            result = mysql_store_result(_mysql);
            if (result == nullptr) {
                LOG(ERROR, "未找到用户信息\n");
                return false;
            }
        }
        // 5. 遍历查询结果，设置到user对象中
        if (mysql_num_rows(result) != 1) {
            LOG(ERROR, "登陆验证失败，查询到的用户信息不唯一\n");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(result);
        user["uid"] = (Json::UInt)std::stoi(row[0]);
        user["score"] = (Json::UInt)std::stoi(row[1]);
        user["total_count"] = (Json::UInt)std::stoi(row[2]);
        user["win_count"] = (Json::UInt)std::stoi(row[3]);
        // 6. 释放结果集并返回
        mysql_free_result(result);
        return true;
    }

    /**
     * @brief 通过用户名查找用户信息
     * @param username  用户名
     * @param user      输出用户信息
     * @return bool     查找成功返回true，失败返回false
     */
    bool find_user_by_username(const std::string &username, Json::Value &user) {
        // 1. 以username为过滤条件构建SQL语句
        char sql[1024]{0};
        sprintf(sql, "select uid, score, total_count, win_count from `user` where username='%s';", username.c_str());
        MYSQL_RES *result = nullptr;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 2. 执行SQL语句
            if (mysql_util::mysql_execute(_mysql, sql) == false) {
                LOG(ERROR, "查找用户的SQL语句执行失败\n");
                return false;
            }
            // 3. 保存结果集到本地
            result = mysql_store_result(_mysql);
            if (result == nullptr) {
                LOG(ERROR, "未找到用户信息\n");
                return false;
            }
        }
        // 4. 遍历结果集并设置到user对象中
        if (mysql_num_rows(result) != 1) {
            LOG(ERROR, "查询失败，用户名对应的用户信息不唯一\n");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(result);
        user["uid"] = (Json::UInt)std::stoi(row[0]);
        user["score"] = (Json::UInt)std::stoi(row[1]);
        user["total_count"] = (Json::UInt)std::stoi(row[2]);
        user["win_count"] = (Json::UInt)std::stoi(row[3]);
        // 5. 释放结果集并返回
        mysql_free_result(result);
        return true;
    }

    /**
     * @brief 通过uid查找用户信息
     * @param uid       用户ID
     * @param user      输出用户信息
     * @return bool     查找成功返回true，失败返回false
     */
    bool find_user_by_uid(uint32_t uid, Json::Value &user) {
        // 1. 以uid为过滤条件构建SQL语句
        char sql[1024]{0};
        sprintf(sql, "select username, score, total_count, win_count from `user` where uid='%u';", uid);
        MYSQL_RES *result = nullptr;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 2. 执行SQL语句
            if (mysql_util::mysql_execute(_mysql, sql) == false) {
                LOG(ERROR, "查找用户的SQL语句执行失败\n");
                return false;
            }
            // 3. 保存结果集到本地
            result = mysql_store_result(_mysql);
            if (result == nullptr) {
                LOG(ERROR, "未找到用户信息\n");
                return false;
            }
        }
        // 4. 遍历结果集并设置到user对象中
        if (mysql_num_rows(result) != 1) {
            LOG(ERROR, "查询失败，UID对应的用户信息不唯一\n");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(result);
        user["username"] = row[0];
        user["score"] = (Json::UInt)std::stoi(row[1]);
        user["total_count"] = (Json::UInt)std::stoi(row[2]);
        user["win_count"] = (Json::UInt)std::stoi(row[3]);
        // 5. 释放结果集并返回
        mysql_free_result(result);
        return true;
    }

    /**
     * @brief 更新胜利用户的分数
     * 胜利时天梯分数+30，对战场次+1，胜利次数+1，其余不变
     * @param uid      用户ID
     * @return bool    更新成功返回true，失败返回false
     */
    bool update_win_user_score(uint32_t uid) {
        // 1. 构建更新的SQL语句
        char sql[1024]{0};
        sprintf(sql, "update `user` set score = score + 30, total_count = total_count + 1, win_count = win_count + 1 where uid = %u;", uid);
        std::unique_lock<std::mutex> lock(_mutex);
        // 2. 执行更新的SQL语句
        if (mysql_util::mysql_execute(_mysql, sql) == false) {
            LOG(ERROR, "更新对战胜利用户分数失败\n");
            return false;
        }
        return true;
    }

    /**
     * @brief 更新失败用户的分数
     * 失败时天梯分数-30，对战场次+1，其余不变
     * @param uid      用户ID
     * @return bool    更新成功返回true，失败返回false
     */
    bool update_fail_user_score(uint32_t uid) {
        // 1. 构建更新的SQL语句
        char sql[1024]{0};
        sprintf(sql, "update `user` set score = score - 30, total_count = total_count + 1 where uid = %u;", uid);
        std::unique_lock<std::mutex> lock(_mutex);
        // 2. 执行更新的SQL语句
        if (mysql_util::mysql_execute(_mysql, sql) == false) {
            LOG(ERROR, "更新对战失败用户分数失败\n");
            return false;
        }
        return true;
    }

private:
    MYSQL *_mysql = nullptr;  // MySQL操作句柄
    std::mutex _mutex;        // 保护多线程操作的互斥锁
};
